#include "ov2640.h"
#include "ov2640cfg.h"

__OV2640_Info OV2640Info;
DCMI_HandleTypeDef *DCMI_hdcmi;
uint8_t OV2640_Init(DCMI_HandleTypeDef *hdcmi) {
    DCMI_hdcmi = hdcmi;
    OV2640_Flash_Ctrl(0);
    OV2640_Flash_Ctrl(1);
    OV2640_Delay(200);
    OV2640_Flash_Ctrl(0);
    OV2640_PWDN(0);
    OV2640_Delay(10);
    OV2640_RST(0);
    OV2640_Delay(200);
    OV2640_RST(1);
    OV2640_Delay(200);
    if (OV2640_ReadID()) {
        return 1;
    }
    OV2640_Config();
    return 0x00;
}

uint8_t OV2640_ReadID(void) {
    SCCB_WR_Reg(OV2640_DSP_RA_DLMT, 0x01);
    OV2640Info.MID = SCCB_RD_Reg(OV2640_SENSOR_MIDH);
    OV2640Info.MID <<= 8;
    OV2640Info.MID |= SCCB_RD_Reg(OV2640_SENSOR_MIDL);
    if (OV2640Info.MID != OV2640_MID) {
        return 1;
    }
    OV2640Info.PID = SCCB_RD_Reg(OV2640_SENSOR_PIDH);
    OV2640Info.PID <<= 8;
    OV2640Info.PID |= SCCB_RD_Reg(OV2640_SENSOR_PIDL);
    if ((OV2640Info.PID != OV2640_PID1) & (OV2640Info.PID != OV2640_PID2)) {
        return 2;
    }
    return 0;
}

void OV2640_Config(void) {
    OV2640_JPEGConfig(JPEG_320x240);
    OV2640_BrightnessConfig(0x20);
    OV2640_AutoExposure(0);
}

void OV2640_Flash_Ctrl(uint8_t sw) {
    if (sw == 0) HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);    //PA5--CH1 ��0
    else HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);          //PA5--CH1 ��1

}

void OV2640_JPEGConfig(uint8_t imgtype) {
    uint32_t i;
    for (i = 0; i < (sizeof(OV2640_JPEG_INIT) / 2); i++) {
        SCCB_WR_Reg(OV2640_JPEG_INIT[i][0], OV2640_JPEG_INIT[i][1]);
    }
    for (i = 0; i < (sizeof(OV2640_YUV422) / 2); i++) {
        SCCB_WR_Reg(OV2640_YUV422[i][0], OV2640_YUV422[i][1]);
    }
    SCCB_WR_Reg(0xff, 0x01);
    SCCB_WR_Reg(0x15, 0x00);
    for (i = 0; i < (sizeof(OV2640_JPEG) / 2); i++) {
        SCCB_WR_Reg(OV2640_JPEG[i][0], OV2640_JPEG[i][1]);
    }
    switch (imgtype) {
        case JPEG_160x120: {
            for (i = 0; i < (sizeof(OV2640_160x120_JPEG) / 2); i++) {
                SCCB_WR_Reg(OV2640_160x120_JPEG[i][0], OV2640_160x120_JPEG[i][1]);
            }
            break;
        }
        case JPEG_176x144: {
            for (i = 0; i < (sizeof(OV2640_176x144_JPEG) / 2); i++) {
                SCCB_WR_Reg(OV2640_176x144_JPEG[i][0], OV2640_176x144_JPEG[i][1]);
            }
            break;
        }
        case JPEG_320x240: {
            for (i = 0; i < (sizeof(OV2640_320x240_JPEG) / 2); i++) {
                SCCB_WR_Reg(OV2640_320x240_JPEG[i][0], OV2640_320x240_JPEG[i][1]);
            }
            break;
        }
        case JPEG_352x288: {
            for (i = 0; i < (sizeof(OV2640_352x288_JPEG) / 2); i++) {
                SCCB_WR_Reg(OV2640_352x288_JPEG[i][0], OV2640_352x288_JPEG[i][1]);
            }
            break;
        }
        case JPEG_800x600: {
            for (i = 0; i < (sizeof(OV2640_800x600_JPEG) / 2); i++) {
                SCCB_WR_Reg(OV2640_800x600_JPEG[i][0], OV2640_800x600_JPEG[i][1]);
            }
            break;
        }
        default: {
            for (i = 0; i < (sizeof(OV2640_160x120_JPEG) / 2); i++) {
                SCCB_WR_Reg(OV2640_160x120_JPEG[i][0], OV2640_160x120_JPEG[i][1]);
            }
            break;
        }
    }
}

void OV2640_BrightnessConfig(uint8_t brightness) {
    SCCB_WR_Reg(0xff, 0x00);
    SCCB_WR_Reg(0x7c, 0x00);
    SCCB_WR_Reg(0x7d, 0x04);
    SCCB_WR_Reg(0x7c, 0x09);
    SCCB_WR_Reg(0x7d, brightness);
    SCCB_WR_Reg(0x7d, 0x00);
}

const static uint8_t OV2640_AUTOEXPOSURE_LEVEL[5][8] =
        {
                {
                        0xFF, 0x01,
                        0x24, 0x20,
                        0x25, 0x18,
                        0x26, 0x60,
                },
                {
                        0xFF, 0x01,
                        0x24, 0x34,
                        0x25, 0x1c,
                        0x26, 0x00,
                },
                {
                        0xFF, 0x01,
                        0x24, 0x3e,
                        0x25, 0x38,
                        0x26, 0x81,
                },
                {
                        0xFF, 0x01,
                        0x24, 0x48,
                        0x25, 0x40,
                        0x26, 0x81,
                },
                {
                        0xFF, 0x01,
                        0x24, 0x58,
                        0x25, 0x50,
                        0x26, 0x92,
                },
        };

void OV2640_AutoExposure(uint8_t level) {
    uint8_t i;
    uint8_t *p = (uint8_t *) OV2640_AUTOEXPOSURE_LEVEL[level];
    for (i = 0; i < 4; i++) {
        SCCB_WR_Reg(p[i * 2], p[i * 2 + 1]);
    }
}

void StartOV2640(uint32_t *JpegBuffer, uint32_t pictureBufferLength) {
    __HAL_DCMI_ENABLE_IT(DCMI_hdcmi,DCMI_IT_FRAME);
    HAL_DCMI_Start_DMA(DCMI_hdcmi, DCMI_MODE_SNAPSHOT, (uint32_t) JpegBuffer, pictureBufferLength);
}

void HAL_DCMI_FrameEventCallback(DCMI_HandleTypeDef *xhdcmi) {
    HAL_DCMI_Suspend(DCMI_hdcmi);
    HAL_DCMI_Stop(DCMI_hdcmi);
}